﻿using System;
using System.Windows;
using System.Collections.Generic;
using System.Windows.Input;
using System.Windows.Media.Animation;
using Microsoft.Win32;
using YTPD.Lib;
using System.Windows.Forms;
using System.ComponentModel;
using System.Threading;
using System.Windows.Controls;
using System.Diagnostics;

namespace YoutubePlaylistDownloader
{
  /// <summary>
  /// Interaction logic for Window1.xaml
  /// </summary>
  public partial class YtpdWindow : Window, IFileChooser, IStateNotifier, INotifyPropertyChanged
  {
    #region Properties used for INotifyPropertyChanged interface

    private string _textBoxText;
    public string TextBoxText
    {
      get { return _textBoxText; }
      set
      {
        _textBoxText = value;
        ShowActionsGridIfNecessary();
      }
    }

    private Visibility _actionsVisible;
    public Visibility ActionsVisible
    {
      get { return _actionsVisible; }
      set
      {
        _actionsVisible = value;
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs("ActionsVisible"));
        }
      }
    }

    private string _currentState;
    public string CurrentState
    {
      get { return _currentState; }
      set
      {
        _currentState = value;
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs("CurrentState"));
        }
      }
    }

    private double _progressBarMaximum;
    public double ProgressBarMaximum
    {
      get { return _progressBarMaximum; }
      set
      {
        _progressBarMaximum = value;
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs("ProgressBarMaximum"));
        }
      }
    }

    private double _progressBarValue;
    public double ProgressBarValue
    {
      get { return _progressBarValue; }
      set
      {
        _progressBarValue = value;
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs("ProgressBarValue"));
        }
      }
    }

    #endregion

    private BackgroundWorker _worker;
    private bool _activatedOnce;
    private string _clipboardText;

    public YtpdWindow()
    {
      InitializeComponent();

      ActionsVisible = Visibility.Hidden;
      textBox.Focus();
    }

    protected override void OnActivated(EventArgs e)
    {
      base.OnActivated(e);

      SetDataContext();

      PopulateTextBoxWithClipboardContents();
    }

    private void SetDataContext()
    {
      if (!_activatedOnce)
      {
        base.DataContext = this;

        _activatedOnce = true;
      }
    }

    private void PopulateTextBoxWithClipboardContents()
    {
      if (isReadyToRecieveClipboardText())
      {
        string clipboardText = GetTextFromClipboard();
        if (isClipboardTextOkToPopulate(clipboardText))
        {
          PutClipboardTextInTextbox(clipboardText);
        }
      }
    }

    private bool isReadyToRecieveClipboardText()
    {
      return textBox.Visibility == Visibility.Visible &&
             System.Windows.Clipboard.ContainsText(System.Windows.TextDataFormat.Text);
    }

    private void PutClipboardTextInTextbox(string clipboardText)
    {
      textBox.Text = clipboardText;
      _clipboardText = clipboardText;
    }

    private static string GetTextFromClipboard()
    {
      string clipboardText = System.Windows.Clipboard.GetText(System.Windows.TextDataFormat.Text).Length > 255 ?
                             System.Windows.Clipboard.GetText(System.Windows.TextDataFormat.Text).Substring(0, 255) :
                             System.Windows.Clipboard.GetText(System.Windows.TextDataFormat.Text);
      return clipboardText;
    }

    private bool isClipboardTextOkToPopulate(string clipboardText)
    {
      var uriConverter = new UriConverter(new WebClientImpl());
      return uriConverter.IsYoutubeUri(clipboardText) &&
             _clipboardText != clipboardText;
    }

    private void showActionGrid()
    {
      ActionsVisible = Visibility.Visible;
      var storyboard = FindResource("Timeline1") as Storyboard;
      if (storyboard != null) BeginStoryboard(storyboard);
    }

    private void hideActionGrid()
    {
      var storyboard = FindResource("Timeline2") as Storyboard;
      if (storyboard != null) BeginStoryboard(storyboard);
    }

    private void slideInProgressBar()
    {
      var storyboard = FindResource("slideInProgressBar") as Storyboard;
      if (storyboard != null) BeginStoryboard(storyboard);
    }

    private void slideOutProgressBar()
    {
      var storyboard = FindResource("slideOutProgressBar") as Storyboard;
      if (storyboard != null) BeginStoryboard(storyboard);
    }

    private void SetActionsVisibilityToHidden(object sender, EventArgs e)
    {
      ActionsVisible = Visibility.Hidden;
    }

    private void downloadPlaylistAsAudio(string uri, string outputDirectory)
    {
      var videos = GetListOfVideosFromPlaylist(uri);

      var downloader = new Downloader(this);
      downloader.AsAudioOnly(outputDirectory, videos);
    }

    private IList<YoutubeVideo> GetListOfVideosFromPlaylist(string uri)
    {
      SetState("Getting list of videos");

      var uriConverter = new UriConverter(new WebClientImpl());
      var playlistId = uriConverter.GetPlaylistId(uri);
      var gdataUri = uriConverter.FromYoutubePlaylistToGDataPlaylist(playlistId);
      var resultAggregator = new ResultAggregator(gdataUri);
      var videos = resultAggregator.GetVideos();

      SetState("Videos found: " + videos.Count);
      return videos;
    }

    private void downloadSingleVideoAsAudio(string uri, string outputDirectory)
    {
      var video = GetVideoFromSingleVideo(uri);

      var downloader = new Downloader(this);
      downloader.AsAudioOnly(outputDirectory, new System.Collections.Generic.List<YoutubeVideo> { video });
    }

    private YoutubeVideo GetVideoFromSingleVideo(string uri)
    {
      SetState("Getting video information");

      var video = YoutubeVideo.FromSingleVideo(uri);
      return video;
    }

    private void downloadAsAudio(object args)
    {
      var uri = ((object[])args)[0] as string;
      var outputDirectory = ((object[])args)[1] as string;

      var uriConverter = new UriConverter(new WebClientImpl());
      if (uriConverter.IsPlaylistUri(uri))
      {
        downloadPlaylistAsAudio(uri, outputDirectory);
      }
      else
      {
        downloadSingleVideoAsAudio(uri, outputDirectory);
      }
    }

    private void downloadPlaylistAsVideo(string uri, string outputDirectory)
    {
      var videos = GetListOfVideosFromPlaylist(uri);

      var downloader = new Downloader(this);
      downloader.AsVideo(outputDirectory, videos);
    }

    private void downloadSingleVideoAsVideo(string uri, string outputDirectory)
    {
      var video = GetVideoFromSingleVideo(uri);

      var downloader = new Downloader(this);
      downloader.AsVideo(outputDirectory, new System.Collections.Generic.List<YoutubeVideo> { video });
    }

    private void downloadAsVideo(object args)
    {
      var uri = ((object[])args)[0] as string;
      var outputDirectory = ((object[])args)[1] as string;

      var uriConverter = new UriConverter(new WebClientImpl());
      if (uriConverter.IsPlaylistUri(uri))
      {
        downloadPlaylistAsVideo(uri, outputDirectory);
      }
      else
      {
        downloadSingleVideoAsVideo(uri, outputDirectory);
      }
    }

    private void audioOnlyClicked(object sender, RoutedEventArgs e)
    {
      var uriConverter = new UriConverter(new WebClientImpl());
      if (!uriConverter.IsYoutubeUri(TextBoxText)) return;

      var outputDirectory = GetDirectoryPath();
      if (string.IsNullOrEmpty(outputDirectory)) return;

      string uri = TextBoxText;
      textBox.Text = string.Empty;
      textBox.Visibility = pasteLabel.Visibility = Visibility.Hidden;
      currentState.Visibility = Visibility.Visible;

      SetProgress(0, 100); //default progress to nothing
      slideInProgressBar();

      _worker = new BackgroundWorker
         {
           WorkerSupportsCancellation = true
         };
      _worker.DoWork += delegate { this.downloadAsAudio(new object[] { uri, outputDirectory }); };
      _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(downloadCompleted);
      _worker.RunWorkerAsync();
    }

    private void videoClicked(object sender, RoutedEventArgs e)
    {
      var uriConverter = new UriConverter(new WebClientImpl());
      if (!uriConverter.IsYoutubeUri(TextBoxText)) return;

      var outputDirectory = GetDirectoryPath();
      if (string.IsNullOrEmpty(outputDirectory)) return;

      string playlistUri = TextBoxText;
      textBox.Text = string.Empty;
      textBox.Visibility = pasteLabel.Visibility = Visibility.Hidden;
      currentState.Visibility = Visibility.Visible;

      SetProgress(0, 100); //default progress to nothing
      slideInProgressBar();

      _worker = new BackgroundWorker
      {
        WorkerSupportsCancellation = true
      };
      _worker.DoWork += delegate { this.downloadAsVideo(new object[] { playlistUri, outputDirectory }); };
      _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(downloadCompleted);
      _worker.RunWorkerAsync();
    }

    private void downloadCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
      if (e.Error != null)
      {
        if (e.Error.GetType() == typeof(System.Xml.XmlException))
        {
          System.Windows.MessageBox.Show("There was a problem with the playlist provided. Please try it again.", "YouTube Playlist Downloader", MessageBoxButton.OK, MessageBoxImage.Error);

          textBox.Visibility = pasteLabel.Visibility = Visibility.Visible;
          currentState.Visibility = Visibility.Hidden;

          slideOutProgressBar();
        }
        else
        {
          System.Windows.MessageBox.Show(e.Error.Message, e.Error.GetType().ToString(), MessageBoxButton.OK, MessageBoxImage.Error);

          textBox.Visibility = pasteLabel.Visibility = Visibility.Visible;
          currentState.Visibility = Visibility.Hidden;

          slideOutProgressBar();
        }
      }
      else if (e.Cancelled == false)
      {
        textBox.Visibility = pasteLabel.Visibility = Visibility.Visible;
        currentState.Visibility = Visibility.Hidden;

        slideOutProgressBar();
      }
    }

    public string GetFilePath(string defaultFileName, string defaultFileExtension)
    {
      var saveFileDialog = new Microsoft.Win32.SaveFileDialog
         {
           AddExtension = true,
           DefaultExt = defaultFileExtension,
           FileName = defaultFileName,
           Filter = "Audio Files|*.mp3;*.wma;",
           InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyMusic),
           Title = "Save audio file",
           ValidateNames = true
         };

      bool? saveChosen = saveFileDialog.ShowDialog(); //invalidoperationexception thrown if filename is invalid
      return saveChosen == true ? saveFileDialog.FileName : string.Empty;
    }

    public string GetDirectoryPath()
    {
      FolderBrowserDialog dialog = new FolderBrowserDialog
         {
           RootFolder = Environment.SpecialFolder.MyComputer,
           Description = "Where do you want to save?",
           ShowNewFolderButton = true
         };
      System.Windows.Forms.DialogResult result = dialog.ShowDialog();
      return (result == System.Windows.Forms.DialogResult.OK) ? dialog.SelectedPath : string.Empty;
    }

    private void ShowActionsGridIfNecessary()
    {
      var uriConverter = new UriConverter(new WebClientImpl());
      if (ActionsVisible == Visibility.Hidden && uriConverter.IsYoutubeUri(TextBoxText))
      {
        showActionGrid();
      }
      else if (ActionsVisible == Visibility.Visible && !uriConverter.IsYoutubeUri(TextBoxText))
      {
        hideActionGrid();
      }
    }

    private void CloseApplication_Click(object sender, EventArgs e)
    {
      if (_worker != null)
      {
        _worker.CancelAsync();
      }

      this.Close();
    }

    public void SetState(string state)
    {
      CurrentState = state;
    }

    public void SetProgress(long current, long total)
    {
      ProgressBarValue = current;
      ProgressBarMaximum = total;
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    private void Thumb_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
    {
      Canvas.SetLeft(this, Canvas.GetLeft(this) + e.HorizontalChange);
      Canvas.SetTop(this, Canvas.GetTop(this) + e.VerticalChange);
    }
  }
}
